
#include <algorithm>

#include "undertopomgr.h"
#include "simulator.h"
#include "eventdefs.h"
#include "node.h"
#include "chordnode.h"
#include "config.h"
#include "random.h"

UnderTopoMgr* UnderTopoMgr::instance_ = NULL;


UnderTopoMgr::~UnderTopoMgr()
{
	std::for_each( nodes_.begin(), nodes_.end(), ReleaseObject() );
	nodes_.clear();
}

bool UnderTopoMgr::create()
{
	NEW_OBJECT( UnderTopoMgr, instance_, () )
	return true;
}

bool UnderTopoMgr::init()
{
	switch( config.undertopo_.topo_source ){
		case Config::TOPO_STATIC:
			return createTopoStatic();
		case Config::TOPO_FILE:
			return createTopoFromFile();
		default:
			assert_or_retv( false, false );
	}
}

void UnderTopoMgr::destroy()
{
	if ( instance_!=NULL ){
		RELEASE_OBJ( instance_ );
	}
}

bool UnderTopoMgr::send( node_id_t src_id, node_id_t dst_id, Packet *packet )
{
	assert_or_retv( packet!=NULL, false );

	bool ret = false;
	Node *src_node = node(src_id);
	Node *dst_node = node(dst_id);

	if ( (src_node!=NULL) && (dst_node!=NULL) ){
		NEW_VAR_OBJECT2( Event, RecvPackEvent, event, (dst_node, packet) )
		Simulator::instance().scheduleEvent( event, latency(src_id,dst_id) );
		RELEASE_OBJ(event)
		ret = true;
	}

	return ret;

}

sim_time_t UnderTopoMgr::latency( node_id_t src_id, node_id_t dst_id )
{
	return 0.01;
}

Node* UnderTopoMgr::node( node_id_t id )
{
	if ( id>=0 && id<(node_id_t)nodes_.size() ){
		return nodes_[id];
	} else{
		return NULL;
	}
}

bool UnderTopoMgr::createTopoStatic()
{
	int network_size = config.undertopo_.network_size;
	assert_or_retv( network_size>0, false );

	std::vector<uint32_t> random_list;
	random_list.resize(network_size);
	uint32_t rand;
	uint32_t count=0;

	while (count < network_size) {
		rand = rng.uniform(0, std::numeric_limits<int>::max());
		if (std::find(random_list.begin(), random_list.end(), rand) == random_list.end()) {
			random_list[count] = rand;
			count++;
		}
	}

#if 0
	for( count=0; count<network_size; count++ ){
		random_list[count] = count;
	}
	std::random_shuffle( random_list.begin(), random_list.end() );
#endif

	int net_num = config.chordgod_.number;
	int nodes_per_chord_net = network_size/net_num;
	int nodes_sched = 0;
	int this_chord_net_size = 0;
	int cur_net_id = 0;
	int cur_net_size = 0;
	int next_net_no = 0 + nodes_per_chord_net;

	for ( int id=0; id<network_size; id++ ){
		if ( cur_net_size>=next_net_no ){
			cur_net_id ++;
			if ( cur_net_id==net_num-1 ) next_net_no = network_size;
			else next_net_no += nodes_per_chord_net;
		} 
		NEW_VAR_OBJECT( ChordNode, node, ( random_list[id], id ) );
		node->setStartupNetID( cur_net_id );
		nodes_.push_back( node );
		cur_net_size++;
	}
	return true;

}

bool UnderTopoMgr::createTopoFromFile()
{
	return false;
}
